/*
 * Copyright  bocmbci
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { NativeToJsMessageQueue } from './NativeToJsMessageQueue';
import { PluginManager } from './PluginManager';

export class CordovaBridge{
  private static readonly LOG_TAG = "CordovaBridge";
  private pluginManager: PluginManager;
  private jsMessageQueue: NativeToJsMessageQueue;
  private expectedBridgeSecret: number = -1;

  private static instance: CordovaBridge;

  public constructor(_pluginManager, _jsMessageQueue) {
    this.pluginManager = _pluginManager;
    this.jsMessageQueue = _jsMessageQueue;

    CordovaBridge.instance = this;
  }

  public static getInstance(): CordovaBridge{
    return CordovaBridge.instance;
  }

  jsExec(bridgeSecret: number, service: string, action: string, callbackId: string, args: string): string{
    if (!this.verifySecret("exec()", bridgeSecret)) {
      return null;
    }
    // 如果未收到参数，则向 JS 发送一条消息。它将切换桥接模式并再次尝试。
    // 发送专门为此情况的消息,以“@”开头，因此无法将相同的字符串编码到其他消息中。
    if (arguments === null) {
      return "@Null arguments.";
    }

    this.jsMessageQueue.setPaused(true);
    try {
      this.pluginManager.exec(service, action, callbackId, args, this);
      let ret: string | null = null;
      if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) {
        ret = this.jsMessageQueue.popAndEncode(false);
      }
      return ret;
    } catch (error) {
      error.printStackTrace();
      return "";
    } finally {
      this.jsMessageQueue.setPaused(false);
    }
  }

  jsSetNativeToJsBridgeMode(bridgeSecret: number, value: number){
    if (!this.verifySecret("setNativeToJsBridgeMode()", bridgeSecret)) {
      return;
    }
    this.jsMessageQueue.setBridgeMode(value);
  }

  jsRetrieveJsMessages(bridgeSecret: number, fromOnlineEvent: boolean): string{
    if (!this.verifySecret("retrieveJsMessages()", bridgeSecret)) {
      return null;
    }
    return this.jsMessageQueue.popAndEncode(fromOnlineEvent);
  }

  private verifySecret(action: string, bridgeSecret: number): boolean {
    if (!this.jsMessageQueue.isBridgeEnabled()) {
      if (bridgeSecret === -1) {
        console.log(CordovaBridge.LOG_TAG, `${action} call made before bridge was enabled.`);
      } else {
        console.log(CordovaBridge.LOG_TAG, `Ignoring ${action} from previous page load.`);
      }
      return false;
    }

    // Bridge secret wrong and bridge not due to it being from the previous page.
    if (this.expectedBridgeSecret < 0 || bridgeSecret != this.expectedBridgeSecret) {
      console.error(CordovaBridge.LOG_TAG, "Bridge access attempt with wrong secret token," +
        " possibly from malicious code. Disabling exec() bridge!");
      this.clearBridgeSecret();
      return false;
    }

    return true;
  }

  public clearBridgeSecret(): void {
    this.expectedBridgeSecret = -1;
  }

  public isSecretEstablished(): boolean {
    return this.expectedBridgeSecret !== -1;
  }

  // 实现 reset() 方法
  public reset(): void {
    this.jsMessageQueue.reset();
    this.clearBridgeSecret();
  }

  promptOnJsPrompt(origin: string, message: string, defaultValue: string){
    if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
      // Protect against random iframes being able to talk through the bridge.
      // Trust only pages which the app would have been allowed to navigate to anyway.
      if (this.pluginManager.shouldAllowBridgeAccess(origin)) {
          // Enable the bridge
          let bridgeMode = parseInt(defaultValue.substring(9));
          this.jsMessageQueue.setBridgeMode(bridgeMode);
          // Tell JS the bridge secret.
          let secret = this.generateBridgeSecret();
          return "" + secret;
      } else {
      }
      return "";
    }
    return null;
  }

  generateBridgeSecret(): number {
    this.expectedBridgeSecret = Math.random() * (Number.MAX_SAFE_INTEGER);
    return this.expectedBridgeSecret;
  }

  getJsMessageQueue(): NativeToJsMessageQueue{
    return this.jsMessageQueue;
  }
}